package org.zoomdev.zoom.protobuf.modules;

import org.zoomdev.zoom.serializer.EmptySerializer;
import org.zoomdev.zoom.serializer.Serializer;
import org.zoomdev.zoom.serializer.SerializerFactory;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

public final class ProtostuffSerializerFactory implements SerializerFactory {

    private final Map<String, Serializer> map = new HashMap<>();


    public Serializer create(Type type) {

        if (type == void.class || type == Void.class) {
            return EmptySerializer.EMPTY;
        }

        if (type instanceof Class) {
            Class clazz = (Class) type;
            if (SchemaUtils.isSpecific(clazz)) {
                return map.computeIfAbsent(type.getTypeName(), k -> {
                    ISchema schema = ISchemaFactory.createFromSimple(clazz);
                    if (schema != null) {
                        return new ISchemaProtostuffSerializer(schema);
                    }
                    if (clazz.isArray()) {
                        return new Serializers.ArrayProtostuffSerializer(clazz.getComponentType());
                    }
                    return new Serializers.SchemaSerializer(clazz);
                });
            }
        }

        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            if (SchemaUtils.checkParameterizedType(parameterizedType)) {
                return map.computeIfAbsent(type.getTypeName(), k -> new Serializers.ListProtostuffSerializer((Class<?>) parameterizedType.getRawType(),
                        ISchemaFactory.create(parameterizedType.getActualTypeArguments()[0])));
            }else{
                Class rawType = (Class) parameterizedType.getRawType();
                return new Serializers.SchemaSerializer(rawType);
            }
        }

        return CommonProtostuffSerializer.SERIALIZER;
    }

    @Override
    public Serializer createWithTypes(Type[] types) {
        for (Type type : types) {
            if (type instanceof Class) {
                if (!SchemaUtils.isSpecific((Class) type)) {
                    return CommonProtostuffSerializer.SERIALIZER;
                }
            }
            if (type instanceof ParameterizedType) {
                if (!SchemaUtils.checkParameterizedType((ParameterizedType) type)) {
                    return CommonProtostuffSerializer.SERIALIZER;
                }
            }
        }
        return map.computeIfAbsent(getKey(types), k -> {
            if (types.length == 0) {
                return EmptySerializer.EMPTY;
            }

            return new ProtostuffArgumentSerializer(types);
        });
    }

    protected String getKey(Type[] types) {
        StringBuilder sb = new StringBuilder(":");
        for (Type type : types) {
            sb.append(type.getTypeName());
        }
        return sb.toString();
    }


}
